package com.xiaoyilin.config;

import com.xiaoyilin.algorithm.TradeRecordSingleKeyDatabaseShardingAlgorithm;
import com.xiaoyilin.algorithm.TradeRecordSingleKeyTableShardingAlgorithm;
import io.shardingjdbc.core.api.ShardingDataSourceFactory;
import io.shardingjdbc.core.api.config.MasterSlaveRuleConfiguration;
import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 读取application.yaml
 * 配置多个数据源
 * 数据源的名称最好要有一定的规则，方便配置分库的计算规则
 * sharding-jdbc的原理
 * sharding-jdbc做一个DataSource给上层应用使用，这个DataSource创建很重要
 * 这个DataSource包含所有的逻辑库和逻辑表，
 * 应用增删改查时，sharding-jdbc自己再修改sql，选择合适的数据库继续操作。
 * 强制路由主库
 * HintManager hintManager = HintManager.getInstance();
 * hintManager.setMasterRouteOnly();
 */
@Configuration
public class DataSourceConfig {

    @Autowired
    private  Environment env;

    @Bean(name = "dataSource0")
    @Qualifier("dataSource0")
    @ConfigurationProperties(prefix="spring.datasource.sharding0")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSource1")
    @Qualifier("dataSource1")
    @ConfigurationProperties(prefix="spring.datasource.sharding1")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 需要手动配置事务管理器
     * @param shardingDataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {
        return new DataSourceTransactionManager(shardingDataSource);
    }

/*    @Bean
    @Primary
    public SqlSessionFactory customSqlSessionFactory(DataSource shardingDataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(shardingDataSource);
       sessionFactory.setTypeAliasesPackage("com.xiaoyilin.entity");
        return sessionFactory.getObject();
    }*/

    /**
     * 逻辑数据库
     * 描述名称与真实数据源映射
     * 并且可以设置默认的数据源，
     * 当表没有配置分库规则时会使用默认的数据源
     */
    @Bean("dataSourceMap")
    public Map<String, DataSource>  dataSourceMap(
                                @Qualifier("dataSource0") DataSource dataSource0,
                                @Qualifier("dataSource1") DataSource dataSource1){
        Map<String, DataSource> dataSourceMap = new HashMap<>(4);
        dataSourceMap.put("dataSource0", dataSource0);
        dataSourceMap.put("dataSource1", dataSource1);
        return  dataSourceMap;
    }

    /**
     * t_trade_record 分库策略
     * @param dataSourceMap
     * ShardingRuleConfiguration是分库分表配置的核心和入口，
     * 它可以包含多个TableRuleConfiguration和MasterSlaveRuleConfiguration
     * 每一组相同规则分片的表配置一个TableRuleConfiguration。
     * 如果需要分库分表和读写分离共同使用，每一个读写分离的逻辑库配置一个MasterSlaveRuleConfiguration。
     * 每个TableRuleConfiguration对应一个ShardingStrategyConfiguration，
     * 它有5中实现类可供选择
     * 仅读写分离使用MasterSlaveRuleConfiguration即可。
     */
    @Primary
    @Bean("shardingDataSource")
    public DataSource shardingRule(@Qualifier("dataSourceMap") Map<String, DataSource> dataSourceMap) throws SQLException {
        // 构建读写分离配置
        MasterSlaveRuleConfiguration masterSlaveRuleConfig = new MasterSlaveRuleConfiguration();
        masterSlaveRuleConfig.setName("ds_0");
        masterSlaveRuleConfig.setMasterDataSourceName("dataSource0");
        masterSlaveRuleConfig.getSlaveDataSourceNames().add("dataSource1");
        // 通过ShardingSlaveDataSourceFactory继续创建ShardingDataSource
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getMasterSlaveRuleConfigs().add(masterSlaveRuleConfig);
        //表规则配置策略是作用在特定的表规则上的，数据源策略与表策略与特定表相关
        TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration();
        tableRuleConfig.setLogicTable("t_trade_record");
        tableRuleConfig.setActualDataNodes("dataSource${0..1}.t_trade_record_${0..2}");
        // 数据库分片算法
        StandardShardingStrategyConfiguration databaseShardingStrategy = new StandardShardingStrategyConfiguration("tradeId",
                TradeRecordSingleKeyDatabaseShardingAlgorithm.class.getName());
        // 配置分库策略
        tableRuleConfig.setDatabaseShardingStrategyConfig(databaseShardingStrategy);
        StandardShardingStrategyConfiguration tableShardingStrategy = new StandardShardingStrategyConfiguration("tradeId",
                TradeRecordSingleKeyTableShardingAlgorithm.class.getName());
        // 配置分表策略
        tableRuleConfig.setTableShardingStrategyConfig(tableShardingStrategy);
        ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();
        //
        shardingRuleConfig.getMasterSlaveRuleConfigs().add(masterSlaveRuleConfig);
        //
        shardingRuleConfiguration.setTableRuleConfigs(Arrays.asList(tableRuleConfig));
        DataSource dataSource =  ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfiguration, new ConcurrentHashMap(), new Properties());
        return  dataSource;
    }



}
